//
//  MPSCRingBuffer.hpp
//  mpscq 多生产者单消费者环形缓冲区
//
//  Created by 董一龙 on 2022/8/7.
//

#ifndef MPSCRingBuffer_hpp
#define MPSCRingBuffer_hpp

#include <mutex>
#include "../util/SpinMutex.hpp"

/**
* @class MPSCMPSCRingBuffer
* @brief 多写单读，静态内存分配有界缓冲区
* @author yilongdong
* @note
* 读者写者之间是cas，写者之间是互斥锁
*/
template <uint32_t BufferSize>
class MPSCRingBuffer {
public:
    MPSCRingBuffer() : m_front(0), m_rear(0), m_size(0) {}
    ~MPSCRingBuffer() = default;
    MPSCRingBuffer(MPSCRingBuffer const&) = delete;
    MPSCRingBuffer(MPSCRingBuffer &&) = delete;
    MPSCRingBuffer & operator=(MPSCRingBuffer& other) = delete;
    MPSCRingBuffer & operator=(MPSCRingBuffer&& other) = delete;
    
    int32_t write(uint8_t const* src, uint32_t bytes);
    
    int32_t read(uint8_t * dst, uint32_t bytes);
    
    uint32_t size() const {
        return m_size;
    }
    
    static constexpr uint32_t capacity() {
        return roundupToNextPowOfTwo(BufferSize);
    }
    
private:
    
    static constexpr uint32_t roundupToNextPowOfTwo(uint32_t num) {
        --num;
        num = num | num >> 1;
        num = num | num >> 2;
        num = num | num >> 4;
        num = num | num >> 8;
        num = num | num >> 16;
        ++num;
        return num;
    }

    uint32_t m_front;
    uint32_t m_rear;
    uint32_t m_size;
    uint8_t m_buffer[roundupToNextPowOfTwo(BufferSize)];
    mutable std::mutex m_mutex_for_writer;
    mutable SpinMutex m_spin_mutex_for_rw;
};

template <uint32_t BufferSize> int32_t MPSCRingBuffer<BufferSize>::write(uint8_t const* src, uint32_t bytes) {
    if(bytes == 0) return 0;
    assert(bytes <=  MPSCRingBuffer<BufferSize>::capacity());
    uint32_t constexpr capacity = MPSCRingBuffer<BufferSize>::capacity();
    uint32_t bytes_to_write = bytes;
    if(bytes_to_write > capacity - m_size) {
        return -1;
    }
    
    std::lock_guard<std::mutex> locker_for_writer(m_mutex_for_writer);
    
    if(bytes_to_write <= capacity - m_rear) {
        memmove(m_buffer + m_rear, src, bytes_to_write);
        m_rear += bytes_to_write;
        m_rear = m_rear & (capacity - 1);
    }
    else {
        uint32_t size_1 = capacity - m_rear;
        memmove(m_buffer + m_rear, src, size_1);
        uint32_t size_2 = bytes_to_write - size_1;
        memcpy(m_buffer, src + size_1, size_2);
        m_rear = size_2;
    }
    
    std::lock_guard<SpinMutex> spin_locker(m_spin_mutex_for_rw);
    m_size += bytes_to_write;
    return bytes_to_write;
}

template <uint32_t BufferSize> int32_t MPSCRingBuffer<BufferSize>::read(uint8_t * dst, uint32_t bytes) {
    if(bytes == 0) return 0;
    
    uint32_t constexpr capacity = MPSCRingBuffer<BufferSize>::capacity();
    uint32_t bytes_to_read = std::min(bytes, m_size);
    if(bytes_to_read < capacity - m_front) {
        memmove(dst, m_buffer + m_front, bytes_to_read);
        m_front += bytes_to_read;
        m_front = m_front & (capacity - 1);
    }
    else {
        const auto size_1 = capacity - m_front;
        memcpy(dst, m_buffer + m_front, size_1);
        const auto size_2 = bytes_to_read - size_1;
        memcpy(dst + size_1, m_buffer, size_2);
        m_front = size_2;
    }
    
    std::lock_guard<SpinMutex> spin_locker(m_spin_mutex_for_rw);
    m_size -= bytes_to_read;
    return bytes_to_read;
}


#endif /* MPSCRingBuffer_hpp */
